package cn.springsoter.core.sms.service.impl;

import cn.springsoter.core.sms.constant.SMSConstant;
import cn.springsoter.core.sms.exception.SMSException;
import cn.springsoter.core.sms.model.dto.BaseSMSMessageDTO;
import cn.springsoter.core.sms.model.dto.DaHanReportRequestDTO;
import cn.springsoter.core.sms.model.dto.SMSMessageDTO;
import cn.springsoter.core.sms.model.dto.SMSTemplateMessageDTO;
import cn.springsoter.core.sms.model.vo.BaseSMSMessageVO;
import cn.springsoter.core.sms.model.vo.SMSMessageVO;
import cn.springsoter.core.sms.service.ISMSService;
import cn.springsoter.core.sms.utils.PhoneUtil;
import cn.springsoter.core.sms.utils.ResponseUtil;
import cn.springsoter.core.sms.utils.JsonUtil;
import cn.springsoter.core.sms.utils.SMSIdUtil;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 大汉三通平台短信实现
 *  注意需要支持jdk6、7、8所以有些写法比较原始
 * @author dws
 * @date 2022-09-06 17:30:00
 */
@Service
@Slf4j
public class DaHanSMSServiceImpl implements ISMSService {
    @Override
    public SMSMessageVO sendSms(SMSMessageDTO smsMessage) {
        return sendSms(smsMessage, false);
    }

    @Override
    public SMSMessageVO sendMarketingSms(SMSMessageDTO smsMessage) {
        return sendSms(smsMessage, true);
    }

    @Override
    public List<BaseSMSMessageVO> sendBatchSms(List<BaseSMSMessageDTO> smsMessages) {
        return sendSms(smsMessages, false);
    }

    @Override
    public List<BaseSMSMessageVO> sendBatchMarketingSms(List<BaseSMSMessageDTO> smsMessages) {
        return sendSms(smsMessages, true);
    }

    @Override
    public SMSMessageVO sendSms(SMSTemplateMessageDTO smsTemplateMessage) {
        return sendSms(smsTemplateMessage, false);
    }

    @Override
    public SMSMessageVO sendMarketingSms(SMSTemplateMessageDTO smsTemplateMessage) {
        return sendSms(smsTemplateMessage, true);
    }

    @Override
    public DaHanReportRequestDTO processCallbackReport(String report) {
        return JsonUtil.toObject(report, DaHanReportRequestDTO.class);
    }

    /**
     * 给一个或多个手机号发送相同内容短信
     *
     * @param smsMessage    短信消息，参考{@link SMSMessageDTO}
     * @param marketing     是否营销性质的短信，{@code true}为营销性质短信，{@code false}为普通短信
     * @return 提交成功时，返回消息标识、回调地址等，参考 {@link SMSMessageVO}，失败时抛出 {@link SMSException} 异常
     * @throws SMSException 参数校验不通过或其他失败情况会抛出这个异常
     */
    private SMSMessageVO sendSms(SMSMessageDTO smsMessage, boolean marketing) {
        validate(smsMessage);

        Map<String, Object> body = toJson(smsMessage);
        String phones = (String) body.get("phones");
        log.info("开始发送短信：手机号->{}，签名->{}，内容->{}", phones, smsMessage.getSign(), smsMessage.getContent());

        // 提交发送短信请求
        body.put("account", marketing ? SMSConstant.DH_MARKETING_ACCOUNT : SMSConstant.DH_ACCOUNT);
        body.put("password", marketing ? SMSConstant.DH_MARKETING_PASSWORD : SMSConstant.DH_PASSWORD);
        JsonNode result;
        try {
            result = ResponseUtil.post(JsonUtil.toJson(body), SMSConstant.DH_BASE_URL + "/json/sms/Submit");
        } catch (Exception e) {
            log.error("短信发送失败", e);
            throw new SMSException("短信发送失败");
        }

        // 提交结果校验
        if (!"0".equals(result.get("result").asText())) {
            String desc = result.get("desc").asText();
            log.error("短信发送失败：手机号->{}, 供应商返回描述->{}", phones, desc);
            throw new SMSException(desc);
        }
        String failPhones = result.get("failPhones").asText();
        if (StringUtils.hasText(failPhones)) {
            log.error("部分手机号短信发送失败：所有手机号->{}，失败的手机号->{}", phones, failPhones);
        } else {
            log.info("短信发送成功：手机号->{}", phones);
        }

        return paramsToVO(body);
    }

    /**
     * 批量发送短信
     *
     * @param smsMessages 批量短信
     * @param marketing   是否营销性质短信，{@code true}为营销性质短信，{@code false}为普通短信
     * @return 提交成功时，返回消息标识，顺序和传入参数保持对应，参考 {@link BaseSMSMessageVO}，失败时抛出 {@link SMSException} 异常
     * @throws SMSException 参数校验不通过或其他失败情况会抛出这个异常
     */
    private List<BaseSMSMessageVO> sendSms(List<BaseSMSMessageDTO> smsMessages, boolean marketing) {
        validate(smsMessages);

        log.info("开始批量发送短信");

        // 提交发送短信请求
        Map<String, Object> body = new HashMap<String, Object>();
        body.put("account", marketing ? SMSConstant.DH_MARKETING_ACCOUNT : SMSConstant.DH_ACCOUNT);
        body.put("password", marketing ? SMSConstant.DH_MARKETING_PASSWORD : SMSConstant.DH_PASSWORD);
        List<Map<String, Object>> data = toJson(smsMessages);
        body.put("data", data);
        JsonNode result;
        try {
            result = ResponseUtil.post(JsonUtil.toJson(body), SMSConstant.DH_BASE_URL + "/json/sms/BatchSubmit");
        } catch (Exception e) {
            log.error("批量发送短信失败", e);
            throw new SMSException("批量发送短信失败");
        }

        // 提交结果校验
        if (!"0".equals(result.get("result").asText())) {
            String desc = result.get("desc").asText();
            log.error("批量发送短信失败： 供应商返回描述->{}", desc);
            throw new SMSException(desc);
        }
        JsonNode resultData = result.get("data");
        if (resultData!=null && !resultData.isNull() && resultData.size()>0) {
            log.error("批量发送短信，部分失败：失败信息->{}", resultData);
        } else {
            log.info("批量发送短信成功");
        }

        return paramsToVO(data);
    }

     /**
     * 给一个或多个手机号发送相同内容的模板短信
     *
     * @param smsTemplateMessage 模板短信消息
     * @param marketing          是否营销性质的短信，{@code true}为营销性质短信，{@code false}为普通短信
     * @return 提交成功时，返回消息标识、回调地址等，参考 {@link SMSMessageVO}，失败时抛出 {@link SMSException} 异常
     * @throws SMSException 参数校验不通过或其他失败情况会抛出这个异常
     */
    private SMSMessageVO sendSms(SMSTemplateMessageDTO smsTemplateMessage, boolean marketing) {
        validate(smsTemplateMessage);
        String templateId = smsTemplateMessage.getTemplateId();
        log.info("开始发送短信：模板->{}", templateId);

        // 提交发送短信请求
        Map<String, Object> body = toJson(smsTemplateMessage);
        body.put("account", marketing ? SMSConstant.DH_MARKETING_ACCOUNT : SMSConstant.DH_ACCOUNT);
        body.put("password", marketing ? SMSConstant.DH_MARKETING_PASSWORD : SMSConstant.DH_PASSWORD);
        JsonNode result;
        try {
            result = ResponseUtil.post(JsonUtil.toJson(body), SMSConstant.DH_BASE_URL + "/json/sms/Submit");
        } catch (Exception e) {
            log.error("短信发送失败", e);
            throw new SMSException("短信发送失败");
        }

        // 提交结果校验
        if (!"0".equals(result.get("result").asText())) {
            String desc = result.get("desc").asText();
            log.error("短信发送失败：模板->{}, 供应商返回描述->{}", templateId, desc);
            throw new SMSException(desc);
        }
        String failPhones = result.get("failPhones").asText();
        if (StringUtils.hasText(failPhones)) {
            log.error("部分手机号短信发送失败：所有手机号->{}，失败的手机号->{}", body.get("phones"), failPhones);
        } else {
            log.info("短信发送成功：模板->{}", templateId);
        }

        return paramsToVO(body);
    }

     /**
     * 短信消息对象转换成大汉三通支持的参数格式
     *
     * @param smsMessage 短信
     * @return 大汉三通支持的参数
     */
    private Map<String, Object> toJson(BaseSMSMessageDTO smsMessage) {
        Map<String, Object> params = new HashMap<String, Object>();
        String msgId = String.valueOf(SMSIdUtil.get().nextId());
        params.put("msgid", msgId);
        params.put("phones", join(smsMessage.getPhones()));
        params.put("params", Collections.singletonMap("ctcTheme", smsMessage.getCompany().name()));
        params.put("sign", "【" + smsMessage.getSign() + "】");
        params.put("content", smsMessage.getContent());
        // 定时发送
        Date sendTime = smsMessage.getSendTime();
        if(sendTime != null && sendTime.after(new Date())) {
            params.put("sendtime", new SimpleDateFormat("yyyyMMddHHmm").format(sendTime));
        }
        // 回调
        if(smsMessage instanceof SMSMessageDTO) {
            SMSMessageDTO messageDTO = (SMSMessageDTO) smsMessage;
            String callbackUrlPre = messageDTO.getCallbackUrlPre();
            if(StringUtils.hasText(callbackUrlPre)) {
                params.put("callbackurl", callbackUrlPre+"/sms/callback/"+msgId);
            }
        }
        return params;
    }

    /**
     * 消息列表转换成大汉三通支持的参数格式
     *
     * @param smsMessages 短信列表
     * @return 大汉三通支持的参数
     */
    private List<Map<String, Object>> toJson(List<BaseSMSMessageDTO> smsMessages) {
        List<Map<String, Object>> data = new ArrayList<Map<String, Object>>();
        for(int i=0; i<smsMessages.size(); i++) {
            data.add(toJson(smsMessages.get(i)));
        }
        return data;
    }

     /**
     * 模板消息转换成大汉三通支持的参数格式
     *
     * @param smsTemplateMessage 模板消息
     * @return 大汉三通支持的参数
     */
    private Map<String, Object> toJson(SMSTemplateMessageDTO smsTemplateMessage) {
        Map<String, Object> res = new HashMap<String, Object>();
        String msgId = String.valueOf(SMSIdUtil.get().nextId());
        res.put("msgid", msgId);
        res.put("phones", join(smsTemplateMessage.getPhones()));
        res.put("params", Collections.singletonMap("ctcTheme", smsTemplateMessage.getCompany().name()));

        // 模板相关转换
        Map<String, Object> template = new HashMap<String, Object>();
        template.put("id", smsTemplateMessage.getTemplateId());
        Map<String, String> variables = smsTemplateMessage.getVariables();
        if (variables != null && !variables.isEmpty()) {
            List<Object> params = new ArrayList<Object>();
            Iterator<Map.Entry<String, String>> it = variables.entrySet().iterator();
            while (it.hasNext()){
                Map.Entry<String, String> entry = it.next();
                Map<String, String> variable = new HashMap<String, String>();
                variable.put("name", entry.getKey());
                variable.put("value", entry.getValue());
                params.add(variable);
            }
            template.put("variables", params);
        }
        res.put("template", template);

        // 定时发送
        Date sendTime = smsTemplateMessage.getSendTime();
        if(sendTime != null && sendTime.after(new Date())) {
            res.put("sendtime", new SimpleDateFormat("yyyyMMddHHmm").format(sendTime));
        }

        // 回调
        String callbackUrlPre = smsTemplateMessage.getCallbackUrlPre();
        if(StringUtils.hasText(callbackUrlPre)) {
            res.put("callbackurl", callbackUrlPre+"/sms/callback/"+msgId);
        }
        return res;
    }

    /**
     * 大汉三通参数转VO对象
     * @param params 大汉三通参数
     * @return      VO对象
     */
    private SMSMessageVO paramsToVO(Map<String, Object> params) {
        SMSMessageVO messageVO = new SMSMessageVO();
        messageVO.setMsgId((String) params.get("msgid"));
        messageVO.setCallbackUrl((String) params.get("callbackurl"));
        return messageVO;
    }

    /**
     * 大汉三通参数转VO对象
     * @param data  大汉三通data参数
     * @return      VO对象列表
     */
    private List<BaseSMSMessageVO> paramsToVO(List<Map<String, Object>> data) {
        List<BaseSMSMessageVO> messageVOS = new ArrayList<BaseSMSMessageVO>(data.size());
        for(int i=0; i<data.size(); i++) {
            Map<String, Object> params = data.get(i);
            BaseSMSMessageVO messageVO = new BaseSMSMessageVO();
            messageVO.setMsgId((String) params.get("msgid"));
            messageVOS.add(messageVO);
        }
        return messageVOS;
    }

    /**
     * 校验普通短信
     * @param smsMessage 短信
     * @throws SMSException 校验不通过抛出这个异常
     */
    private void validate(BaseSMSMessageDTO smsMessage) {
        validatePhones(smsMessage.getPhones());
        if(smsMessage.getCompany() == null) {
            throw new SMSException("短信无法发送，因为缺少公司信息");
        }
        String sign = smsMessage.getSign();
        if(!StringUtils.hasText(sign)) {
            throw new SMSException("短信无法发送，因为缺少签名信息");
        }
        if(sign.startsWith("【") || sign.startsWith("[")) {
            throw new SMSException("短信无法发送，因为签名使用了括号包裹");
        }
        String content = smsMessage.getContent();
        if (!StringUtils.hasText(content)) {
            throw new SMSException("短信无法发送，因为缺少短信内容");
        }
        if(content.length() > 1000) {
            throw new SMSException("短信无法发送，因为短信长度超过1000");
        }

        if(smsMessage instanceof SMSMessageDTO) {
            SMSMessageDTO messageDTO = (SMSMessageDTO) smsMessage;
            String callbackUrlPre = messageDTO.getCallbackUrlPre();
            if (StringUtils.hasText(callbackUrlPre) && callbackUrlPre.endsWith("/")) {
                throw new SMSException("短信无法发送，因为回调前缀不能以/结尾");
            }
        }
    }

    /**
     * 校验批量短信
     * @param smsMessages 短信列表
     * @throws SMSException 校验不通过抛出这个异常
     */
    private void validate(List<BaseSMSMessageDTO> smsMessages) {
        if (CollectionUtils.isEmpty(smsMessages)) {
            throw new SMSException("短信无法发送，因为要发送的列表为空");
        }
        if(smsMessages.size() > 1000) {
            throw new SMSException("短信无法发送，因为要发送的列表大小超过了1000个");
        }
        for(int i=0; i<smsMessages.size(); i++) {
            validate(smsMessages.get(i));
        }
    }

    /**
     * 校验模板短信
     * @param smsTemplateMessage 模板短信
     * @throws SMSException 校验不通过抛出这个异常
     */
    private void validate(SMSTemplateMessageDTO smsTemplateMessage) {
        if (smsTemplateMessage == null) {
            throw new SMSException("短信无法发送，因为短信模板消息为null");
        }
        validatePhones(smsTemplateMessage.getPhones());
        if(smsTemplateMessage.getCompany() == null) {
            throw new SMSException("短信无法发送，因为缺少公司信息");
        }
        if (!StringUtils.hasText(smsTemplateMessage.getTemplateId())) {
            throw new SMSException("短信无法发送，因为模板id为空");
        }
        String callbackUrlPre = smsTemplateMessage.getCallbackUrlPre();
        if (StringUtils.hasText(callbackUrlPre) && callbackUrlPre.endsWith("/")) {
            throw new SMSException("短信无法发送，因为回调前缀不能以/结尾");
        }
    }

    private void validatePhones(List<String> phones) {
        if(CollectionUtils.isEmpty(phones)) {
            throw new SMSException("短信无法发送，因为缺少手机号");
        }
        if(phones.size() > 1000) {
            throw new SMSException("短信无法发送，因为手机号数量超过了1000个");
        }
        for(int i=0; i<phones.size(); i++) {
            String phone = phones.get(i);
            if(!PhoneUtil.isPhone(phone)) {
                throw new SMSException("短信无法发送，因为存在错误手机号:" + phone);
            }
        }
    }

    private String join(List<String> list) {
        StringBuilder sb = new StringBuilder();
        for (int i=0; i<list.size(); i++) {
            String str = list.get(i);
            if(i > 0) {
                sb.append(",");
            }
            sb.append(str);
        }
        return sb.toString();
    }
}
